package br.edu.fatec.ads.tcc.acesso;

import javacard.framework.APDU;
import javacard.framework.Applet;
import javacard.framework.ISO7816;
import javacard.framework.ISOException;
import javacard.framework.JCSystem;
import javacard.framework.Util;

public class AcessoApplet extends Applet {
	
	private static byte ACESSO_CLA = 0x20;
	private static final byte GET_CODIGO_CARTAO = 0x01;
	private static final byte SET_CODIGO_CARTAO = 0x02;
	private static final byte GET_PERMISSOES_ACESSO = 0x03;
	private static final byte SET_PERMISSOES_ACESSO = 0x04;
	private static final byte ACESSO_SALA = 0x05;

	private static final byte FIRST_POSITION = 0x00;
	private static final byte SECOND_POSITION = 0x01;
	
	private static final short EXCEPTION_ACESSO_NEGADO = 0x6984;
	
	private byte[] permissoesAcesso;
	private byte[] codigoCartao;
	private byte[] indice;
	
	private AcessoApplet() {
		permissoesAcesso = new byte[120];
		codigoCartao = new byte[2];
		indice = new byte[] { 0x00, 0x00 };
	}

	public static void install(byte bArray[], short bOffset, byte bLength)
			throws ISOException {
		new AcessoApplet().register();
	}

	public void process(APDU apdu) throws ISOException {

		byte[] buffer = apdu.getBuffer();
		
		if(this.selectingApplet()){	return;	}
		
		if(buffer[ISO7816.OFFSET_CLA] != ACESSO_CLA){ ISOException.throwIt(ISO7816.SW_CLA_NOT_SUPPORTED);	}
		
		switch(buffer[ISO7816.OFFSET_INS]){
			case GET_CODIGO_CARTAO:{
				this.getCodigoCartao(apdu);
				break;
			}
			
			case SET_CODIGO_CARTAO:{
				this.setCodigoCartao(apdu);
				break;
			}
			
			case GET_PERMISSOES_ACESSO:{
				this.getPermissoesAcesso(apdu);
				break;
			}
			
			case SET_PERMISSOES_ACESSO:{
				this.setPermissoesAcesso(apdu);
				break;
			}
			
			case ACESSO_SALA:{
				this.acessarSala(apdu);
				break;
			}
		
			default:{
				ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED);
				break;
			}
		}
		
	}
	
	private void getCodigoCartao(APDU apdu){
		apdu.setOutgoing();
		
		JCSystem.beginTransaction();
		
		try{
			apdu.setOutgoingLength((short) codigoCartao.length);
			
			apdu.sendBytesLong(codigoCartao, (short) 0, (short) codigoCartao.length);
			
			JCSystem.commitTransaction();
		}catch (ISOException cause) {
			JCSystem.abortTransaction();
			
			ISOException.throwIt(cause.getReason());
		}
	}
	
	private void setCodigoCartao(APDU apdu){
		byte[] buffer = apdu.getBuffer();

		short tamanhoParam = apdu.setIncomingAndReceive();
		
		if(tamanhoParam != (short) 2 ){ ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); }
		
		JCSystem.beginTransaction();
		
		try{
			Util.arrayCopy(buffer, ISO7816.OFFSET_CDATA, codigoCartao, (short) 0, (short) 2);
			
			JCSystem.commitTransaction();
		}catch(ISOException cause){
			JCSystem.abortTransaction();
			
			ISOException.throwIt(cause.getReason());
		}
		
	}
	
	private void getPermissoesAcesso(APDU apdu){
		apdu.setOutgoing();
		
		JCSystem.beginTransaction();
		
		try{
			apdu.setOutgoingLength((short) permissoesAcesso.length);
			
			apdu.sendBytesLong(permissoesAcesso, (short) 0, (short) permissoesAcesso.length);
			
			JCSystem.commitTransaction();
		}catch (ISOException cause) {
			JCSystem.abortTransaction();
			
			ISOException.throwIt(cause.getReason());
		}
	}
	
	private void setPermissoesAcesso(APDU apdu){
		byte[] buffer = apdu.getBuffer();

		JCSystem.beginTransaction();
		
		try{
			
			short tamanhoParam = apdu.setIncomingAndReceive();
			
			if(tamanhoParam % 2 != (short) 0){ ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); }
			
			for(short i = 0; i < tamanhoParam; i+=2){				
				short indiceAtualizado = this.verificarIndice(indice);
				
				if(indiceAtualizado != -1){					
					Util.arrayCopy(buffer, (short) (ISO7816.OFFSET_CDATA + i), permissoesAcesso, indiceAtualizado, (short) 2);
				}else{
					ISOException.throwIt(ISO7816.SW_FILE_FULL);
				}
			}
			
			JCSystem.commitTransaction();
		}catch (ISOException cause) {
			JCSystem.abortTransaction();
			
			ISOException.throwIt(cause.getReason());
		}
	}
	
	private short verificarIndice(byte[] indice){
		for(short i = 0 ; i < permissoesAcesso.length; i+=2){
			if((permissoesAcesso[i] == indice[FIRST_POSITION]) && (permissoesAcesso[(short)(i+1)] == indice[SECOND_POSITION])){
				return (short) i;
			}
		}
		
		return (short) -1;
	}
	
	private void acessarSala(APDU apdu){
		byte[] buffer = apdu.getBuffer();
		
		short tamanhoCodigoSala = apdu.setIncomingAndReceive();
		
		if(tamanhoCodigoSala != 2){	ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);	}
		
		JCSystem.beginTransaction();
		
		try{			
			byte[] codigoSala = new byte[] { (byte) (buffer[ISO7816.OFFSET_CDATA] & 0xff), (byte)((buffer[ISO7816.OFFSET_CDATA + SECOND_POSITION] >> 8) & 0xff) };
			
			short indiceAcesso = this.verificarIndice(codigoSala);
			
			if(indiceAcesso == -1){
				ISOException.throwIt(EXCEPTION_ACESSO_NEGADO);
			}
			
			JCSystem.commitTransaction();
		}catch (ISOException cause) {
			JCSystem.abortTransaction();
			
			ISOException.throwIt(cause.getReason());
		}

	}

}